aboutsummaryrefslogtreecommitdiffstats
path: root/src/app/game/[slug]/page.tsx
blob: d9f431f2fc4170e2f73c8ae1e1b4e896decbc878 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"use client";
import { Suspense } from "react";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import Link from "next/link";
import { MdLibraryMusic } from "react-icons/md";
import { GameGlobalStyle, GameRoot, GameNavbar, GameContent, BackgroundVideo } from "./page.styles";
import { useGameEngine } from "./hooks/useGameEngine";
import PreGameView from "./components/PreGameView";
import PlayingView from "./components/PlayingView";
import ResultsView from "./components/ResultsView";

function GameInner() {
  const engine = useGameEngine();

  const {
    // refs
    audioRef, videoRef, charRowRef, charRefs,
    // song
    audioUrl, songTitle, songArtist, isVideo, isReady, loadingLrc,
    // phase
    phase, countdown,
    // game state
    g, gameLines, accuracy, wpm,
    // timing
    currentMs, duration, progressPct, lineTimingPct, lineRemainingMs,
    currentLineTime, intermissionData,
    // display
    wrongChar, clearShowing, comboAnimKey, wrapSpaceIndicators,
    // settings
    backgroundOpacity, setBackgroundOpacity, audioVolume, setAudioVolume,
    isPreviewPlaying,
    // handlers
    handleStart, handleRestart, handlePreviewToggle,
  } = engine;

  return (
    <GameRoot>
      <ToastContainer theme="dark" />

      {!isVideo && (
        <audio ref={audioRef} src={audioUrl || undefined} preload="auto" />
      )}
      {isVideo && (
        <BackgroundVideo
          ref={videoRef}
          src={audioUrl || undefined}
          preload="auto"
          playsInline
          style={{ opacity: backgroundOpacity / 100 }}
        />
      )}

      <GameNavbar style={{ justifyContent: "space-between" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
          <Link
            href="/"
            style={{
              display: "flex", alignItems: "center", gap: 8,
              textDecoration: "none", color: "#ffffff", fontWeight: 700, fontSize: 15,
            }}
          >
            <MdLibraryMusic style={{ fontSize: 20, color: "#a78bfa" }} />
            TypingMIXX
          </Link>
          <Link href="/" style={{ fontSize: 13, color: "rgba(255,255,255,0.6)", textDecoration: "none" }}>
            Home
          </Link>
        </div>
      </GameNavbar>

      <GameContent style={{ position: "relative" }}>
        {phase === "idle" && (
          <PreGameView
            isReady={isReady}
            loadingLrc={loadingLrc}
            songTitle={songTitle}
            songArtist={songArtist}
            audioUrl={audioUrl}
            isVideo={isVideo}
            audioVolume={audioVolume}
            setAudioVolume={setAudioVolume}
            backgroundOpacity={backgroundOpacity}
            setBackgroundOpacity={setBackgroundOpacity}
            isPreviewPlaying={isPreviewPlaying}
            onStart={handleStart}
            onPreviewToggle={handlePreviewToggle}
          />
        )}

        {(phase === "countdown" || phase === "playing") && (
          <PlayingView
            phase={phase}
            countdown={countdown}
            g={g}
            accuracy={accuracy}
            wpm={wpm}
            gameLines={gameLines}
            currentMs={currentMs}
            duration={duration}
            progressPct={progressPct}
            lineTimingPct={lineTimingPct}
            lineRemainingMs={lineRemainingMs}
            currentLineTime={currentLineTime}
            intermissionData={intermissionData}
            wrongChar={wrongChar}
            clearShowing={clearShowing}
            comboAnimKey={comboAnimKey}
            wrapSpaceIndicators={wrapSpaceIndicators}
            charRowRef={charRowRef}
            charRefs={charRefs}
            onRestart={handleRestart}
          />
        )}

        {phase === "finished" && (
          <ResultsView
            g={g}
            accuracy={accuracy}
            wpm={wpm}
            songTitle={songTitle}
            onPlayAgain={handleRestart}
          />
        )}
      </GameContent>
    </GameRoot>
  );
}

export default function GamePage() {
  return (
    <>
      <GameGlobalStyle />
      <Suspense
        fallback={
          <GameRoot>
            <div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 18, color: "rgba(255,255,255,0.5)" }}>
              Loading...
            </div>
          </GameRoot>
        }
      >
        <GameInner />
      </Suspense>
    </>
  );
}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage